这一章开始真正解析xml的标签,逻辑不复杂,但是内容比较多,所有单独放一篇文章。继续上一章的BeanDefinitionDocumentReader.registerBeanDefinitions(),该接口只有一个实现类就是:DefaultBeanDefinitionDocumentReader。以下面xml为例
1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache ="http://www.springframework.org/schema/cache" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd" > <import resource ="classpath*:/spring/job-timer.xml" /> <bean id ="some" class ="src.com.Some" /> <alias name ="some" alias ="someJava,oneBean,twoBean" /> </beans >
区分xml结构定义 registerBeanDefinitions()处理逻辑从xml的根节点开始。调用doRegisterBeanDefinitions(),判断xml是否为spring所定义的xml结构,分别处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public void registerBeanDefinitions (Document doc, XmlReaderContext readerContext ) { this .readerContext = readerContext; logger.debug("Loading bean definitions" ); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); } protected void doRegisterBeanDefinitions (Element root ) { BeanDefinitionParserDelegate parent = this .delegate ; this .delegate = createDelegate(getReaderContext(), root, parent); if (this .delegate .isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return ; } } } preProcessXml(root); parseBeanDefinitions(root, this .delegate ); postProcessXml(root); this .delegate = parent; } protected void parseBeanDefinitions (Element root, BeanDefinitionParserDelegate delegate ) { if (delegate .isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0 ; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate .isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate ); } else { delegate .parseCustomElement(ele); } } } } else { delegate .parseCustomElement(root); } }
从上面代码看出,xml的文档接口,可以有我们自定义,也就是我们所说的DTD。spring默认的结构定义使用XSD,是基于 XML 的 DTD 替代者。
使用spring的xml结构解析 对xml结构解析是从parseDefaultElement()开始的,该方法分别对import、alias、bean、beans标签进行路由调用指定的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private void parseDefaultElement(Element ele , BeanDefinitionParserDelegate delegate ) { if (delegate.nodeNameEquals(ele , IMPORT_ELEMENT) ) { importBeanDefinitionResource(ele ) ; } else if (delegate.nodeNameEquals(ele , ALIAS_ELEMENT) ) { processAliasRegistration(ele ) ; } else if (delegate.nodeNameEquals(ele , BEAN_ELEMENT) ) { processBeanDefinition(ele , delegate ) ; } else if (delegate.nodeNameEquals(ele , NESTED_BEANS_ELEMENT) ) { do RegisterBeanDefinitions(ele ) ; } }
解析 import 标签 importBeanDefinitionResource(),先获得resource属性的值,也就是需要导入的资源路径。判断该路径是否为绝对路径还是相对路径分别处理。逻辑跟ioc启动时传入的location地址处理方式大相径庭,先解析地址获得Resource对象,然后继续往下解析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 protected void importBeanDefinitionResource (Element ele) { String location = ele.getAttribute(RESOURCE_ATTRIBUTE); if (!StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty" , ele); return ; } location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); Set<Resource> actualResources = new LinkedHashSet<>(4 ); boolean absoluteLocation = false ; try { absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); } catch (URISyntaxException ex) { } if (absoluteLocation) { try { int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]" ); } } catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to import bean definitions from URL location [" + location + "]" , ele, ex); } } else { try { int importCount; Resource relativeResource = getReaderContext().getResource().createRelative(location); if (relativeResource.exists ()) { importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } else { String baseLocation = getReaderContext().getResource().getURL().toString(); importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); } if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]" ); } } catch (IOException ex) { getReaderContext().error("Failed to resolve current resource location" , ele, ex); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]" , ele, ex); } } Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size ()]); getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); }
解析 alias 标签 processAliasRegistration()先获取到name、alias属性的值,非空处理之后,调用registerAlias()向IOC中注册别名。registerAlias()的逻辑为:将别名保存到Map中,由alias映射到name。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 protected void processAliasRegistration(Element ele) { // 获得name 属性、alias 属性 // 例如:例子中 name ="some" alias ="someJava,oneBean,twoBean" String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); boolean valid = true ; // 非空处理,做标记 if (!StringUtils.hasText(name )) { getReaderContext().error("Name must not be empty", ele); valid = false ; } if (!StringUtils.hasText(alias )) { getReaderContext().error("Alias must not be empty", ele); valid = false ; } if (valid ) { try { // 向容器中注册别名 getReaderContext().getRegistry().registerAlias(name , alias ); } catch (Exception ex) { getReaderContext().error("Failed to register alias '" + alias + "' for bean with name '" + name + "'", ele, ex); } // 发送别名处理完的事件 getReaderContext().fireAliasRegistered(name , alias , extractSource(ele)); } } // SimpleAliasRegistry public void registerAlias(String name , String alias ) { // 非空处理 Assert .hasText(name , "'name' must not be empty"); Assert .hasText(alias , "'alias' must not be empty"); // 如果alias 和name 一致,相当于没有取别名。 // remove已注册的别名,节省别名映射的性能 if (alias .equals(name )) { this.aliasMap.remove(alias ); } else { String registeredName = this.aliasMap.get (alias ); // 如果已注册该别名,则判断之前注册的和本次注册的是否指向同一个bean。否则抛异常 // 之所以要判断之前注册的和本次注册的是否指向同一个bean,是因为在最后注册BeanDefinition要兼容其他的IOC容器,也会调用registerAlias() if (registeredName != null ) { if (registeredName.equals(name )) { // An existing alias - no need to re-register return ; } if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } } checkForAliasCircle(name , alias ); // 注册别名 this.aliasMap.put(alias , name ); } }
解析 bean 标签 processBeanDefinition()先将bean标签解析成BeanDefinitionHolder,再将BeanDefinitionHolder注册到IOC中。而BeanDefinitionHolder是对BeanDefinition的封装。如果说BeanDefinition是bean的描述,那么BeanDefinitionHolder则是对这个描述取一个名字,告诉这个BeanDefinition属于谁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected void processBeanDefinition(Element ele , BeanDefinitionParserDelegate delegate ) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele ) ; if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele , bdHolder ) ; try { BeanDefinitionReaderUtils . registerBeanDefinition(bdHolder , getReaderContext () .getRegistry() ); } catch (BeanDefinitionStoreException ex) { getReaderContext() .error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'" , ele, ex); } getReaderContext() .fireComponentRegistered(new BeanComponentDefinition(bdHolder ) ); } }
其中最重要的两个方法parseBeanDefinitionElement()和registerBeanDefinition(),这里不方便细讲,bean标签里面的子标签、属性太多,留给一下章细说。